package com.ywz.project.system.service.impl;

import cn.hutool.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ywz.common.JWTUtils;
import com.ywz.common.ResultResp;
import com.ywz.common.StringUtils;
import com.ywz.framework.security.CustomUser;
import com.ywz.project.base.system.entity.TSysLoginLog;
import com.ywz.project.base.system.entity.TSysUser;
import com.ywz.project.base.system.service.TSysLoginLogService;
import com.ywz.project.base.system.service.TSysUserService;
import com.ywz.project.system.dto.req.LoginReq;
import com.ywz.project.system.dto.req.RegisterReq;
import com.ywz.project.system.service.LoginService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private TSysLoginLogService sysLoginLogService;
    @Resource
    private TSysUserService sysUserService;

    @Override
    public ResultResp login(LoginReq req) {
        // 验证用户名和密码是否为空
        if (StringUtils.isEmpties(req.getUsername(), req.getPassword()))
            return ResultResp.error("用户名或密码不能为空");

        // 获取当前账号的登陆次数
        String count = redisTemplate.opsForValue().get(req.getUsername());
        if (!StringUtils.isEmpty(count) && Integer.parseInt(count) >= 5) {
            return ResultResp.error("登录失败次数过多，请稍后再试");
        }

        // 将表单数据封装到 UsernamePasswordAuthenticationToken，进行登录认证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(req.getUsername(), req.getPassword());
        Authentication authenticate = null;
        try {
            // authenticate方法会调用security包里UserDetailsServiceImpl的loadUserByUsername方法
            authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        } catch (Exception e) {
            if (StringUtils.isEmpty(count)) {
                redisTemplate.opsForValue().set(req.getUsername(), "1", 5, TimeUnit.MINUTES);
            } else {
                redisTemplate.opsForValue().set(req.getUsername(), String.valueOf(Integer.parseInt(count) + 1), 5, TimeUnit.MINUTES);
            }
            return ResultResp.error(e.getMessage());
        }

        // ----------------------登录校验成功，返回响应数据------------------
        Map<String, Object> resultMap = new HashMap<>();
        // 登录成功获取用户角色权限
        CustomUser customUser = (CustomUser) authenticate.getPrincipal();
        Collection<? extends GrantedAuthority> authorities = authenticate.getAuthorities();
        StringBuilder authoritiesStr = new StringBuilder();
        for (GrantedAuthority authority : authorities) {
            authoritiesStr.append(authority.getAuthority());
            authoritiesStr.append(StringUtils.SEPARATOR);
        }
        TSysUser sysUser = customUser.getSysUser();
        // 校验通过返回token
        Map<String, Object> tokenMap = new HashMap<>();
        tokenMap.put("userId", sysUser.getId());
        tokenMap.put("username", sysUser.getUsername());
        tokenMap.put("authorities", authoritiesStr.toString());
        String token = JWTUtils.getToken(tokenMap);
        resultMap.put("token", token);
        resultMap.put("name", sysUser.getName());
        // 将token存入redis
        redisTemplate.opsForValue().set("login:" + sysUser.getId(), token, 1, TimeUnit.DAYS);
        // 记录登录日志
        saveLoginLog(sysUser);
        return ResultResp.success(resultMap);
    }

    @Override
    public ResultResp register(RegisterReq req) {
        // 验证用户名和密码是否为空
        if (StringUtils.isEmpties(req.getUsername(), req.getPassword()))
            return ResultResp.error("用户名或密码不能为空");

        // 校验用户名是否存在
        LambdaQueryWrapper<TSysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TSysUser::getUsername, req.getUsername());
        TSysUser existsOne = sysUserService.getOne(queryWrapper, false);
        if (Objects.nonNull(existsOne))
            return ResultResp.error("用户名已存在");

        // 添加用户信息
        TSysUser sysUser = new TSysUser();
        BeanUtils.copyProperties(req, sysUser);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        sysUser.setPassword(encoder.encode(req.getPassword()));
        sysUser.setStatus(1); // 设置账号为已启动
        sysUser.setCreateTime(LocalDateTime.now());
        sysUserService.save(sysUser);
        return ResultResp.success();
    }

    @Override
    public ResultResp logout() {
        JWT currentTokenInfo = JWTUtils.getCurrentTokenInfo();
        if (Objects.isNull(currentTokenInfo)) {
            return ResultResp.error("token失效");
        }
        String userId = currentTokenInfo.getPayload("userId").toString();
        redisTemplate.delete("login:" + userId);
        return ResultResp.success();
    }

    @Override
    public void resetPassword(String oldPassword, String newPassword) {
        if (StringUtils.isEmpties(oldPassword, newPassword))
            throw new RuntimeException("原密码或新密码不能为空");

        JWT currentTokenInfo = JWTUtils.getCurrentTokenInfo();
        if (Objects.isNull(currentTokenInfo))
            throw new RuntimeException("token失效");

        // 获取登录的帐号
        String username = currentTokenInfo.getPayload("username").toString();
        LambdaQueryWrapper<TSysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TSysUser::getUsername, username);
        queryWrapper.eq(TSysUser::getIsDeleted, 0);
        TSysUser sysUser = sysUserService.getOne(queryWrapper, false);
        if (Objects.isNull(sysUser))
            throw new RuntimeException("用户不存在");

        // 校验密码
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(oldPassword, sysUser.getPassword()))
            throw new RuntimeException("原密码错误");

        // 修改密码
        sysUser.setPassword(encoder.encode(newPassword));
        sysUserService.updateById(sysUser);
    }

    /**
     * 方法描述 -> 记录登入日志
     *
     * @param sysUser 用户实体类
     * @Author: ywz
     * @Date: 2024/09/27
     */
    private void saveLoginLog(TSysUser sysUser) {
        TSysLoginLog loginLog = new TSysLoginLog();
        // 获取request
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        if (sra != null) {
            HttpServletRequest request = sra.getRequest();
            loginLog.setClientIp(request.getRemoteAddr());
        }
        loginLog.setUserId(sysUser.getId().toString());
        loginLog.setUserName(sysUser.getName());
        loginLog.setCreateTime(LocalDateTime.now());
        sysLoginLogService.save(loginLog);
    }
}
